package com.cysoft.starter.log.core.configuration;

import com.cysoft.starter.log.config.LogAutoConfiguration;
import com.cysoft.starter.log.core.aspect.LogAspect;
import com.cysoft.starter.log.core.exception.LogException;
import com.cysoft.starter.log.core.properties.OperateLogProperties;
import com.cysoft.starter.log.core.properties.output.ElasticSearchProperties;
import com.cysoft.starter.log.core.repository.impl.ElasticsearchRepository;
import lombok.extern.slf4j.Slf4j;
import org.frameworkset.elasticsearch.boot.BBossESStarter;
import org.frameworkset.elasticsearch.client.ClientInterface;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author 玖零
 * @version V0.0.1
 * @project cysoft-spring-boot-starter-log
 * @package com.cysoft.starter.log.core.configuration
 * @date 2025-02-27 14:09
 * @desc
 */
@Slf4j
@Configuration
@ConditionalOnBean(name = "elasticsearchConfiguration")
@AutoConfigureAfter(LogAutoConfiguration.class)
public class ElasticsearchConfiguration {

    /**
     * 操作日志配置信息
     */
    private final OperateLogProperties operateLogProperties;

    /**
     * ES链接
     */
    private final BBossESStarter bBossESStarter;

    /**
     * es日志切面配置 构造方法
     * @param operateLogProperties
     */
    public ElasticsearchConfiguration(OperateLogProperties operateLogProperties,BBossESStarter bBossESStarter) {
        log.info("初始化esConfiguration，注入操作日志配置信息和数据源配置信息");
        this.operateLogProperties = operateLogProperties;
        this.bBossESStarter = bBossESStarter;
    }


    /**
     * 注册 Elasticsearch 操作日志切面的 Bean。
     * 该方法会进行一系列操作，包括获取 Elasticsearch 客户端、检查配置、校验索引等，然后构建并返回日志切面 Bean。
     * @return 构建好的 LogAspect 实例，用于处理 Elasticsearch 操作日志相关的切面逻辑。
     */
    @Bean
    public LogAspect elasticsearchLogAspect(){
        log.info("******************开始初始化elasticsearchLogAspect******************");
        //构建es客户端
        ClientInterface restClient =  bBossESStarter.getRestClient();
        //获取elasticSearch配置信息
        ElasticSearchProperties elasticSearchProperties = operateLogProperties.getOutput().getElasticsearch();
        log.info("成功获取elasticSearch配置信息");
        //校验elasticSearch配置信息
        this.checkConfig(restClient,elasticSearchProperties);
        log.info("elasticSearch配置信息校验通过");
        //构建elasticSearch日志切面
        log.info("开始构建elasticSearch日志切面");
        LogAspect logAspect = new LogAspect(new ElasticsearchRepository(restClient, elasticSearchProperties), operateLogProperties);
        log.info("elasticSearch日志切面构建成功，日志切面对象: {}", logAspect);
        return logAspect;
    }

    /**
     * 检查 Elasticsearch 配置信息的有效性，包括连接是否有效以及索引是否存在等。
     * 如果连接无效会抛出 LogException 异常，如果索引不存在会尝试创建索引。
     * @param elasticsearchClient 用于与 Elasticsearch 交互的客户端接口实例
     * @param elasticSearchProperties Elasticsearch 相关的配置信息对象
     */
    private void checkConfig(ClientInterface elasticsearchClient, ElasticSearchProperties elasticSearchProperties) {
        // 校验ES连接是否有效
        if (!isElasticSearchConnectionValid(elasticsearchClient)) {
            throw new LogException("ES连接异常");
        }

        // 校验索引是否存在
        verifyAndCreateIndexIfNotExists(elasticsearchClient, elasticSearchProperties);

    }

    /**
     * 校验 Elasticsearch 中指定索引是否存在，如果不存在则创建该索引。
     *
     * @param elasticsearchClient 用于与 Elasticsearch 进行交互的客户端接口
     * @param elasticsearchConfig 包含 Elasticsearch 相关配置信息的属性对象，如索引名称、索引映射等
     */
    private void verifyAndCreateIndexIfNotExists(ClientInterface elasticsearchClient, ElasticSearchProperties elasticsearchConfig) {
        // 获取要校验的索引名称
        String indexName = elasticsearchConfig.getIndexName();
        // 记录日志，输出正在校验的索引名称
        log.info("正在校验索引 [{}] 是否存在", indexName);
        // 调用 Elasticsearch 客户端的方法，检查指定名称的索引是否存在
        boolean isIndexExists = elasticsearchClient.existIndice(indexName);
        // 记录日志，输出索引是否存在的校验结果
        log.info("索引 [{}] 是否存在的校验结果: {}", indexName, isIndexExists);

        // 如果索引不存在
        if (!isIndexExists) {
            // 记录日志，提示正在创建索引
            log.info("索引 [{}] 不存在，开始创建该索引", indexName);
            // 获取索引的映射信息
            String indexMapping = elasticsearchConfig.getIndexMapping();
            // 调用 Elasticsearch 客户端的方法，根据索引名称和索引映射信息创建索引
            String createIndexResult = elasticsearchClient.createIndiceMapping(indexName, indexMapping);
            // 记录日志，输出创建索引操作的返回结果
            log.info("创建索引 [{}] 的结果: {}", indexName, createIndexResult);
        }
    }

    /**
     * 验证 Elasticsearch 连接是否有效。
     * 该方法通过向 Elasticsearch 发送获取集群健康信息的请求来验证连接。
     * @param restClient 用于与 Elasticsearch 进行交互的客户端接口
     * @return 如果能获取到集群健康信息的响应则返回 true，否则返回 false
     */
    public boolean isElasticSearchConnectionValid(ClientInterface restClient) {
        try {
            // 记录开始验证连接的日志
            log.info("开始验证 Elasticsearch 连接是否有效，尝试获取集群健康信息");
            // 发送请求获取集群信息
            String response = restClient.executeHttp("_cluster/health", ClientInterface.HTTP_GET);
            // 记录获取到的集群健康信息响应
            log.debug("成功获取 Elasticsearch 集群健康信息响应: {}", response);

            // 检查集群状态是否为 red 并给出相应提示
            if (response != null && response.contains("\"status\":\"red\"")) {
                log.warn("Elasticsearch 集群状态为 red，可能存在数据丢失或不可用的情况，请检查集群状态！");
            }

            return true;
        } catch (Exception e) {
            // 记录连接失败的详细日志，包括异常信息
            log.error("连接 Elasticsearch 失败，无法获取集群健康信息", e);
            return false;
        }
    }


}
